package cc.rengu.igas.channel.alipay.core.packswitch;


import cc.rengu.igas.channel.alipay.common.constant.AlipayConfigConstant;
import cc.rengu.igas.channel.alipay.common.constant.AlipayParamConstant;
import cc.rengu.igas.channel.alipay.common.constant.AlipayTreeNodeConstant;
import cc.rengu.igas.channel.alipay.common.enums.RespCodeEnum;
import cc.rengu.igas.channel.alipay.core.realize.AlipaySignService;
import cc.rengu.igas.channel.alipay.core.realize.impl.AlipaySignServiceImpl;
import cc.rengu.igas.channel.alipay.facade.base.BaseRequest;
import cc.rengu.jradp.packetswitch.IncomingPackSwitchImpl;
import cc.rengu.jradp.packetswitch.OutgoingPackSwitchImpl;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.BizMsgInfoMapper;
import cc.rengu.oltp.service.common.dao.CertInfoMapper;
import cc.rengu.oltp.service.common.dao.SecPlanInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.BizMsgInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.CertInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SecPlanInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.*;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.realize.SysParamService;
import cc.rengu.oltp.service.realize.impl.SysParamServiceImpl;
import cc.rengu.oltp.utility.util.ConvertUtil;
import cc.rengu.oltp.utility.util.DateUtil;
import cc.rengu.oltp.utility.util.RandomUtil;
import cc.rengu.oltp.utility.util.XmlTreeUtil;
import cc.rengu.utility.base.TimeUtil;
import cc.rengu.utility.log.RgLog;
import cc.rengu.utility.log.RgLogger;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;

import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.net.URLEncoder;
import java.util.*;

/**
 * 与银联第三方通讯解包前、解包后、组包前及组后的处理类
 * Created by
 */
public class CupsAliPayClientPackSwitch implements IncomingPackSwitchImpl, OutgoingPackSwitchImpl {

    @Override
    public byte[] beforeInComPackSwitch(byte[] bytes) throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String jsonBefore = new String(bytes);
        String msgRegisterFlag = xmlTreeUtil.getXmlTreeStringValue(AlipayTreeNodeConstant.ALIP_MSG_REG_FLAG);
        if (AppParamConstant.YES.equals(msgRegisterFlag)) {
            /*更新业务报文信息*/
            updateBizMsgInfo(rglog, jsonBefore);
        }
        JSONObject jsonObj = JSON.parseObject(jsonBefore, Feature.OrderedField);
        /*通过平台转换会把转义字符去掉，导致验签失败*/
        for (Map.Entry<String, Object> entry : jsonObj.entrySet()) {
            String key = entry.getKey();
            if (key.endsWith("_response")) {
                JSONObject response = (JSONObject) entry.getValue();
                rglog.debug("response:<{}>", response.toString());
                xmlTreeUtil.setXmlTreeStringValue("responseBlock", response.toString());
                break;
            }
        }
        return bytes;
    }

    @Override
    public int afterInComPackSwitch() throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String sign = xmlTreeUtil.getXmlTreeStringValue("sign");
        if (null == sign) {
            rglog.debug("无签域，不进行签名校验!");
            return 0;
        }
        /* 获取数字签名字符串 */
        String signBlock = xmlTreeUtil.getXmlTreeStringValue("responseBlock");
        rglog.debug("加签Block:<{}>", signBlock);
        rglog.debug("sign:<{}>", sign);
        String secPlanInfoListString = xmlTreeUtil.getXmlTreeStringValue(AlipayTreeNodeConstant.SEC_PLAN_INFO_LIST);
        List<SecPlanInfo> secPlanInfoList = JSONArray.parseArray(secPlanInfoListString, SecPlanInfo.class);
        Optional<SecPlanInfo> checkSignSecPlanOpt = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(AlipayParamConstant.VERIFY_CERT_TYPE)).findFirst();
        if (checkSignSecPlanOpt.isPresent()) {
            CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
            CertInfo certInfo = certInfoMapper.selectCertInfoByPrimaryKey(checkSignSecPlanOpt.get().getInstId() + checkSignSecPlanOpt.get().getSecPlanId() + checkSignSecPlanOpt.get().getSecPlanType());
            if (certInfo == null) {
                rglog.error("根据索引<{}>获取验签公钥失败!", checkSignSecPlanOpt.get().getInstId() + checkSignSecPlanOpt.get().getSecPlanId() + checkSignSecPlanOpt.get().getSecPlanType());
                xmlTreeUtil.setXmlTreeStringValue(AlipayTreeNodeConstant.ALI_RET_CODE, RespCodeEnum.VERIFY_SIGN_ERROR.getRespCode());
                xmlTreeUtil.setXmlTreeStringValue(AlipayTreeNodeConstant.ALI_RET_MSG, RespCodeEnum.VERIFY_SIGN_ERROR.getRespCode());
                return 0;
            }
            /* 验证签名 */
            AlipaySignService alipaySignService = new AlipaySignServiceImpl();
            if (alipaySignService.verifySignString(sign, signBlock, certInfo)) {
                rglog.info("签名验证成功!");
                return 0;
            } else {
                rglog.error("签名验证失败!");
                xmlTreeUtil.setXmlTreeStringValue(AlipayTreeNodeConstant.ALI_RET_CODE, RespCodeEnum.VERIFY_SIGN_ERROR.getRespCode());
                xmlTreeUtil.setXmlTreeStringValue(AlipayTreeNodeConstant.ALI_RET_MSG, RespCodeEnum.VERIFY_SIGN_ERROR.getRespCode());
                return 0;
            }
        } else {
            rglog.error("获取验签公钥失败,签名验证失败!");
            xmlTreeUtil.setXmlTreeStringValue(AlipayTreeNodeConstant.ALI_RET_CODE, RespCodeEnum.VERIFY_SIGN_ERROR.getRespCode());
            xmlTreeUtil.setXmlTreeStringValue(AlipayTreeNodeConstant.ALI_RET_MSG, RespCodeEnum.VERIFY_SIGN_ERROR.getRespCode());
            return 0;
        }
    }

    @Override
    public int beforeOutgoPackSwitch() throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID);
        if (instId == null) {
            instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_INST_ID);
        }
        SysParamService sysParamService = new SysParamServiceImpl();
        SysParam sysParam = null;
        /* 加载公共信息 */
        sysParam = sysParamService.getSysParamInfo(instId, AlipayConfigConstant.CUPS_ALIPAY_CONF_NODE, AlipayConfigConstant.ALIPAY_NOTICE_URL);
        if (sysParam != null) {
            xmlTreeUtil.setXmlTreeStringValue(AlipayConfigConstant.ALIPAY_NOTICE_URL, sysParam.getParamValue());
        }
        sysParam = sysParamService.getSysParamInfo(instId, AlipayConfigConstant.CUPS_ALIPAY_CONF_NODE, AlipayConfigConstant.ALIPAY_VERSION);
        if (sysParam != null) {
            xmlTreeUtil.setXmlTreeStringValue(AlipayConfigConstant.ALIPAY_VERSION, sysParam.getParamValue());
        } else {
            xmlTreeUtil.setXmlTreeStringValue(AlipayConfigConstant.ALIPAY_VERSION, "1.0");
        }
        sysParam = sysParamService.getSysParamInfo(instId, AlipayConfigConstant.CUPS_ALIPAY_CONF_NODE, AlipayConfigConstant.ALIPAY_SIGN_TYPE);
        if (sysParam != null) {
            xmlTreeUtil.setXmlTreeStringValue(AlipayConfigConstant.ALIPAY_SIGN_TYPE, sysParam.getParamValue());
        } else {
            xmlTreeUtil.setXmlTreeStringValue(AlipayConfigConstant.ALIPAY_SIGN_TYPE, "RSA2");
        }
        sysParam = sysParamService.getSysParamInfo(instId, AlipayConfigConstant.CUPS_ALIPAY_CONF_NODE, AlipayConfigConstant.ALIPAY_ALGORITHM);
        if (sysParam != null) {
            xmlTreeUtil.setXmlTreeStringValue(AlipayConfigConstant.ALIPAY_ALGORITHM, sysParam.getParamValue());
        }
        sysParam = sysParamService.getSysParamInfo(instId, AlipayConfigConstant.CUPS_ALIPAY_CONF_NODE, AlipayConfigConstant.ALIPAY_CHARSET);
        if (sysParam != null) {
            xmlTreeUtil.setXmlTreeStringValue(AlipayConfigConstant.ALIPAY_CHARSET, sysParam.getParamValue());
        } else {
            xmlTreeUtil.setXmlTreeStringValue(AlipayConfigConstant.ALIPAY_CHARSET, "UTF-8");
        }
        String time = TimeUtil.getDateTime("yyyy-MM-dd HH:mm:ss");
        xmlTreeUtil.setXmlTreeStringValue(AlipayTreeNodeConstant.TIMESTAMP, time);

        /* 获取通道配置信息 */
        DstChannelCfg dstChannelCfg = (DstChannelCfg) xmlTreeUtil.getXmlTreeObjectValue(AlipayTreeNodeConstant.DST_CHANNEL_CFG, DstChannelCfg.class);
        /* 根据通道安全计划获取安全计划 */
        SecPlanInfoMapper secPlanInfoMapper = new SecPlanInfoMapperImpl();
        List<SecPlanInfo> secPlanInfoList = secPlanInfoMapper.selectSecPlanInfoBySecPlanId(instId, dstChannelCfg.getSecPlanId());
        /* 获取敏感信息加密证书 */
        Optional<SecPlanInfo> secPlanInfoOpt = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(AlipayParamConstant.SIGN_CERT_TYPE)).findFirst();
        if (secPlanInfoOpt.isPresent()) {
            xmlTreeUtil.setXmlTreeStringValue(AlipayTreeNodeConstant.SEC_PLAN_INFO_LIST, JSON.toJSONString(secPlanInfoList));
            CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
            CertInfo certInfo = certInfoMapper.selectCertInfoByPrimaryKey(instId + secPlanInfoOpt.get().getSecPlanId() + secPlanInfoOpt.get().getSecPlanType());
            /* 新旧版本标识 */
            sysParam = sysParamService.getSysParamInfo(instId, AlipayConfigConstant.CUPS_ALIPAY_CONF_NODE, AlipayConfigConstant.IS_NEW_VERSION);
            if (sysParam != null && AppParamConstant.YES.equals(sysParam.getParamValue())) {
                /*新版报文支持*/
                rglog.debug("新版接口新增字段<{}>,<{}>", certInfo.getCertId(), dstChannelCfg.getProviderMchntNo());
                xmlTreeUtil.setXmlTreeStringValue(AlipayTreeNodeConstant.CERT_ID, certInfo.getCertId());
                xmlTreeUtil.setXmlTreeStringValue(AlipayTreeNodeConstant.ALIPAY_ORGPID, dstChannelCfg.getProviderMchntNo());
            }
        }

        String nonceStr = RandomUtil.randomString(32);
        xmlTreeUtil.setXmlTreeStringValue("nonceStr", nonceStr);
        return 0;
    }

    @Override
    public byte[] afterOutgoPackSwitch(byte[] bytes) throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        xmlTreeUtil.setXmlTreeStringValue("_MESSAGEHEAD/http/Content-Type", "application/json;charset=utf-8");
        String jsonBefore = new String(bytes);
        String signData = null;
        String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID);
        if (instId == null) {
            instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_INST_ID);
        }
        String msgRegisterFlag = xmlTreeUtil.getXmlTreeStringValue(AlipayTreeNodeConstant.ALIP_MSG_REG_FLAG);
        rglog.debug("未URL编码前发送报文:<{}>", jsonBefore);
        /* 生成生成数字签名的字符串 */
        String signBlockString = generateSignBlockString(jsonBefore);
        if (null == signBlockString) {
            rglog.error("生成加签Block失败!");
            throw new BizException(RespCodeEnum.GENERATE_SIGN_BLOCK_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_BLOCK_ERROR.getRespDesc());
        }
        rglog.debug("加签Block:<{}>", signBlockString);
        /* 获取通道配置信息 */
        DstChannelCfg dstChannelCfg = (DstChannelCfg) xmlTreeUtil.getXmlTreeObjectValue(AlipayTreeNodeConstant.DST_CHANNEL_CFG, DstChannelCfg.class);
        /* 根据通道安全计划获取安全计划 */
        SecPlanInfoMapper secPlanInfoMapper = new SecPlanInfoMapperImpl();
        List<SecPlanInfo> secPlanInfoList = secPlanInfoMapper.selectSecPlanInfoBySecPlanId(instId, dstChannelCfg.getSecPlanId());
        /* 获取敏感信息加密证书 */
        Optional<SecPlanInfo> secPlanInfoOpt = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(AlipayParamConstant.SIGN_CERT_TYPE)).findFirst();
        if (secPlanInfoOpt.isPresent()) {
            xmlTreeUtil.setXmlTreeStringValue(AlipayTreeNodeConstant.SEC_PLAN_INFO_LIST, JSON.toJSONString(secPlanInfoList));
            CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
            CertInfo certInfo = certInfoMapper.selectCertInfoByPrimaryKey(instId + secPlanInfoOpt.get().getSecPlanId() + secPlanInfoOpt.get().getSecPlanType());
            rglog.debug("证书内容<{}>", JSON.toJSONString(certInfo));
            /* 加签 */
            AlipaySignService alipaySignService = new AlipaySignServiceImpl();
            signData = alipaySignService.generateSignString(signBlockString, certInfo);
            rglog.debug("数字签名:<{}>", signData);
        }
        if (signData == null) {
            rglog.debug("生成数字签名失败");
            throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
        }
        String charset = xmlTreeUtil.getXmlTreeStringValue(AlipayConfigConstant.ALIPAY_CHARSET);
        /* urlcode */
        String afterUrlCodeMsg = JsonStringUrlEncoder(jsonBefore, charset, signData);
        rglog.debug("URL编码后发送报文:<{}>", afterUrlCodeMsg);
        /*登记业务报文表*/
        if (AppParamConstant.YES.equals(msgRegisterFlag)) {
            registerBizMsgInfo(rglog, afterUrlCodeMsg);
        }
        return afterUrlCodeMsg.getBytes();

    }


    /**
     * 生成加签Block
     *
     * @param jsonString 原始报文
     * @return 加签BLOCK
     */
    private String generateSignBlockString(String jsonString) {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        try {
            if (null != jsonString && 0 != jsonString.trim().length()) {
                Map maps = (Map) JSON.parse(jsonString);
                ArrayList<String> arrayList = new ArrayList<>();
                int i = 0;
                Object key;
                Object value;
                for (Object map : maps.entrySet()) {
                    key = ((Map.Entry) map).getKey();
                    value = ((Map.Entry) map).getValue();
                    if (!"sign".equals(key) && null != value) {
                        arrayList.add(key + "=" + value + "&");
                    }
                    i++;
                }
                String[] strArray = arrayList.toArray(new String[arrayList.size()]);
                Arrays.sort(strArray);
                StringBuffer stringBuffer = new StringBuffer();
                for (i = 0; i < strArray.length; i++) {
                    stringBuffer.append(strArray[i]);
                }
                return stringBuffer.substring(0, stringBuffer.length() - 1).toString();
            }
        } catch (Exception e) {
            rglog.error("系统异常!");
            return null;
        }
        return null;
    }

    /**
     * json字符串URLEncoder
     *
     * @param jsonString json字符串
     * @return URLEncoder字符串
     */
    private String JsonStringUrlEncoder(String jsonString, String charset, String signData) {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        try {
            if (null != jsonString && 0 != jsonString.trim().length()) {
                Map maps = (Map) JSON.parse(jsonString);
                int i = 0;
                Object key;
                Object value;
                StringBuffer stringBuffer = new StringBuffer();
                for (Object map : maps.entrySet()) {
                    key = ((Map.Entry) map).getKey();
                    value = ((Map.Entry) map).getValue();
                    String urlEncodValue = URLEncoder.encode(value.toString(), charset);
                    if (!"sign".equals(key) && null != value) {
                        stringBuffer.append(key + "=" + urlEncodValue + "&");
                    }
                    i++;
                }
                //添加sign
                String signUrlEncodValue = URLEncoder.encode(signData, charset);
                stringBuffer.append("sign" + "=" + signUrlEncodValue + "&");

                return stringBuffer.substring(0, stringBuffer.length() - 1).toString();
            }
        } catch (Exception e) {
            rglog.error("系统异常!");
            return null;
        }
        return null;
    }

    private void registerBizMsgInfo(RgLogger rglog, String bizMsg) {
        try {
            XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
            if (bizMsg.length() > 8000) {
                rglog.error("存储转发报文超过8K，暂不登记到存储转发表:<{}>", bizMsg);
                return;
            }
            BizMsgInfo bizMsgInfo = new BizMsgInfo();
            BizMsgInfoMapper bizMsgInfoMapper = new BizMsgInfoMapperImpl();
            BaseRequest baseRequest = new BaseRequest();
            ConvertUtil.convertOutput(baseRequest);
            bizMsgInfo.setInstId(baseRequest.getHeader().getInstId());
            bizMsgInfo.setChannelId(xmlTreeUtil.getXmlTreeStringValue(AlipayTreeNodeConstant.CHANNEL_ID));
            bizMsgInfo.setTransSeqNum(baseRequest.getHeader().getTraceNo());
            bizMsgInfo.setTxnDate(baseRequest.getHeader().getTransDate());
            bizMsgInfo.setTxnTime(baseRequest.getHeader().getTransTime());
            bizMsgInfo.setDstTxnNum(baseRequest.getHeader().getTxnNum());
            bizMsgInfo.setCreateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
            if (bizMsg.length() < 4000) {
                bizMsgInfo.setBizReqMsg(bizMsg);
            } else {
                bizMsgInfo.setBizReqMsg(bizMsg.substring(0, 4000));
                bizMsgInfo.setBizReqExpandMsg(bizMsg.substring(4000));
            }
            int iReturnCode = bizMsgInfoMapper.insertBizMsgInfo(bizMsgInfo);
            if (0 != iReturnCode) {
                return;
            }
            xmlTreeUtil.setXmlTreeObjectValue(AlipayTreeNodeConstant.ALIP_BIZ_MSG_INFO, bizMsgInfo);
        } catch (Exception e) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            e.printStackTrace(new PrintWriter(byteArrayOutputStream, true));
            rglog.error("登记业务报文信息表异常:<{}>", byteArrayOutputStream.toString());
        }
    }

    private void updateBizMsgInfo(RgLogger rglog, String bizRspMsg) {
        try {
            XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
            if (bizRspMsg.length() > 8000) {
                rglog.error("存储转发报文超过8K，暂不登记到存储转发表:<{}>", bizRspMsg);
                return;
            }
            if (!xmlTreeUtil.isExist(AlipayTreeNodeConstant.ALIP_BIZ_MSG_INFO)) {
                rglog.error("获取请求业务报文节点失败，不更新");
                return;
            }
            BizMsgInfo bizMsgInfo = (BizMsgInfo) xmlTreeUtil.getXmlTreeObjectValue(AlipayTreeNodeConstant.ALIP_BIZ_MSG_INFO, BizMsgInfo.class);
            BizMsgInfoMapper bizMsgInfoMapper = new BizMsgInfoMapperImpl();
            bizMsgInfo.setUpdateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
            if (bizRspMsg.length() < 4000) {
                bizMsgInfo.setBizRspMsg(bizRspMsg);
            } else {
                bizMsgInfo.setBizRspMsg(bizRspMsg.substring(0, 4000));
                bizMsgInfo.setBizRspExpandMsg(bizRspMsg.substring(4000));
            }
            int iReturnCode = bizMsgInfoMapper.updateBizMsgInfoByTransSeqNum(bizMsgInfo);
            if (0 != iReturnCode) {
                return;
            }
        } catch (Exception e) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            e.printStackTrace(new PrintWriter(byteArrayOutputStream, true));
            rglog.error("登记业务报文信息表异常:<{}>", byteArrayOutputStream.toString());
        }
    }
}
